package com.maylann.ratiocheater.encoder;

import org.apache.commons.codec.net.URLCodec;
import org.springframework.stereotype.Component;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.security.DigestOutputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * Decoding torrent files.
 * by Maylann
 */
@Component
public class TorrentDecoder {

    private static final String fileExtension = ".torrent";


    /**
     * For decoding the information in at .torrent file and return the information in a torrent dictionary map.
     *
     * @param torrentFilename
     *                  The path + filename of the file to decode.
     * @return
     *                  The torrent dictionary map containing all information read in the .torrent file.
     * @throws IOException
     *                  When exception while reading file. Can for example be an {@link FileNotFoundException}
     */
    public TorrentDictionary<String, Object> decode(String torrentFilename) throws IOException {
        validate(torrentFilename);
        return BEncode.deBEncode(new FileInputStream(torrentFilename));
    }

    /**
     * Get the urlencoded infohash from the torrent dictionary.
     *
     * @param in
     *          Should be the "info" dictionary from the whole TorrentDictionary.
     * @return
     *          The infoHash as an urlencoded string.
     */
    public String getInfoHashUrlEncoded(TorrentDictionary<String, Object> in) {
        try {
            DigestOutputStream dos = new DigestOutputStream(new ByteArrayOutputStream(), MessageDigest.getInstance("SHA1"));
            BEncode.bEncode(in, dos);
            return new String(new URLCodec("UTF-8").encode(dos.getMessageDigest().digest()));
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("Unable to get infoHash", e);
        } catch (IOException e) {
            throw new RuntimeException("Error bencoding infohash", e);
        }
    }

    /**
     * Validates whether the specified torrentFilename and filetype is valid.
     *
     * @param torrentFilename
     *                  The path + name of the file to validate.
     */
    private void validate(String torrentFilename) {
        if (torrentFilename == null ) {
            throw new IllegalArgumentException("No torrentFilename was specified.");
        }
        if (torrentFilename.trim().endsWith(fileExtension)) {
            throw new IllegalArgumentException("The specified file is not at valid filetype for torrent decoding:" +
                                                torrentFilename + " - only " + fileExtension + " files are allowed");
        }
    }
}
